package chat_server;

// -----------------------------------------------------------------------------
// Importanweisungen
import for_all.PopUp;
import for_all.Message;
import for_all.Transporter;
import for_all.User;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Hashtable;


public class ManageUser implements Serializable {
    
    // -------------------------------------------------------------------------
    // Variablendeklarationen
    private Hashtable<String,User> users;
    private ArrayList<String> onlineUsers;
    public ServerGui serverGUI;
    
    // -------------------------------------------------------------------------
    // Konstruktor
    public ManageUser (ServerGui serverGUI) {
        
        this.users = new Hashtable<>();
        this.onlineUsers = new ArrayList<>();
        this.serverGUI = serverGUI;
    }
    
    
    // -------------------------------------------------------------------------
    // Benutzer anhand des Namens finden
    public User getUser (String name) {
        if (this.users.containsKey(name)) {
            return this.users.get(name);
        } else {
            return null;
        } 
    }
    
    
    // -------------------------------------------------------------------------
    // einloggen
    public boolean logIn (User user, Transporter trans, ManageRooms mangRms) {
        
        // Benutzer konnte nicht gefunden werden --> registrieren
        if (this.getUser(user.getUserName()) == null) {
            
            //  Alle Benutzer und Räume zum Senden an User sammeln
            String[] userNames = this.getAllOnlineUser();
            String[] roomNames = new String[userNames.length];
            for(int i=0; i<userNames.length; i++) {
                roomNames[i] = users.get(userNames[i]).getRoomName();
            }
            
            user.setLogInAnswer(User.LoginResult.nonRegistrated, user.getUserName(), userNames, roomNames); 
            trans.sendObject(user.loginAnswer);                                      // 0. LogIn Answer senden
            
            this.registrate(user, true);                                             // 1. User registrieren
            this.getUser(user.getUserName()).setOnline(true);                        // 2. online setzten
            this.sendNewUserLoggedOnToAll(user.getUserName(), user.getRoomName());   // 3. neuer online User an alle schicken
            this.onlineUsers.add(user.getUserName());                                // 4. in online Users aufnehmen
            this.getUser(user.getUserName()).setClientHandler(trans, this, mangRms); // 5. Thread für Netzwerkkommunikation starten         
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                PopUp fail = new PopUp(serverGUI, "InterruptedException: " + ex.getMessage(), "Fehler in Login", PopUp.MessageTyp.Error);
            }
            this.serverGUI.setUser(this.getUser(user.getUserName()), true);          // 6. GUI setzen
            return true;
            
            
        // gesperrt
        } else if (this.getUser(user.getUserName()).getUserStatus() == User.UserStati.geblockt) {
            
            user.setLogInAnswer(User.LoginResult.banned, user.getUserName(), null, null);
            trans.sendObject(user.loginAnswer);
            return false;
            
            
        // falsches Passwort        
        } else if (!this.getUser(user.getUserName()).checkPassword(user)) {
            
            user.setLogInAnswer(User.LoginResult.wrongPassword, user.getUserName(), null, null);
            trans.sendObject(user.loginAnswer);
            return false;
            
            
        // Benutzer schon online    
        } else if (this.getUser(user.getUserName()).getOnline() && this.onlineUsers.contains(user.getUserName())) {
            
            user.setLogInAnswer(User.LoginResult.alreadyOnline, user.getUserName(), null, null);
            trans.sendObject(user.loginAnswer);
            return false;

            
        // alles ok, benutzer wird eingeloggt!    
        } else {
            
            //  Alle Benutzer und Räume zum Senden an User sammeln
            String[] userNames = this.getAllOnlineUser();
            String[] roomNames = new String[userNames.length];
            for(int i=0; i<userNames.length; i++) {
                roomNames[i] = users.get(userNames[i]).getRoomName();
            }
            
            user.setLogInAnswer(User.LoginResult.accepted, user.getUserName(), userNames, roomNames);
            trans.sendObject(user.loginAnswer);                                                      
            
            this.getUser(user.getUserName()).setOnline(true);                            // 1. online setzten
            this.sendNewUserLoggedOnToAll(user.getUserName(), user.getRoomName());       // 2. neuer online User an alle schicken
            this.onlineUsers.add(user.getUserName());                                    // 3. in online Users aufnehmen
            this.getUser(user.getUserName()).setClientHandler(trans, this, mangRms);     // 4. Thread für Netzwerkkommunikation starten
            this.serverGUI.setUser(this.getUser(user.getUserName()), true);              // 5. GUI setzen
            this.getUser(user.getUserName()).setUserStatus(User.UserStati.normal, true); // 6. Status auf normal zurückstufen (kann nur verwarnt oder getrennt sein)
            return true;
        
        }        
    }

    
    // -------------------------------------------------------------------------    
    // ausloggen
    public boolean logOut (User user, boolean serverNotification) {
        
        if (this.getUser(user.getUserName()) == null || !this.getUser(user.getUserName()).getOnline() || !this.onlineUsers.contains(user.getUserName())) {
        
            return false;   // User nicht gefunden oder nicht online
        
        } else {
            
            this.getUser(user.getUserName()).setOnline(false);                    // 1. User offline setzen
            this.onlineUsers.remove(user.getUserName());                          // 2. User aus onlineUsers nehmen
            this.sendUserLoggedOffToAll(user.getUserName(), user.getRoomName());  // 3. aktualisierten User an alle online Users senden
            this.getUser(user.getUserName()).removeAllPrivateChats();             // 4. Alle verbleibenden Private Chats entfernen
            this.serverGUI.server.rooms.removeUser(user.getRoomName(), user);     // 5. User aus Raum, in dem er evtl. ist nehmen
            this.getUser(user.getUserName()).setRoom("");                         // 6. Raum bei User entfernen
            this.serverGUI.setUser(this.getUser(user.getUserName()), serverNotification);       // 7. GUI-setzen
            this.getUser(user.getUserName()).getClientHandler().interrupt();      // 8. Client-Handler beenden
            this.getUser(user.getUserName()).clearClientHandler();                // 9. Client-Handelr entfernen
            return true;
        
        }
        
    }
    
    
    // -------------------------------------------------------------------------
    // registrieren
    public boolean registrate (User user, boolean notification) {
        
        // nur registrieren, wenn User noch nicht vorhanden
        if (this.getUser(user.getUserName()) != null) {
            return false;
        } else {
        
            this.users.put(user.getUserName(), user);
            
            if (notification) {
                this.serverGUI.setMessage(new Message( user.getUserName() + " hat sich registriert.", "Admin", "Server", false));          
            } else {
                this.serverGUI.setUser(user, false);
            }
            return true;
        }
    }
    
    
    // -------------------------------------------------------------------------
    // Clients informieren, wenn sich neuer Client anmeldet
    private void sendNewUserLoggedOnToAll(String usr, String room) {
        
        // an alle onlineUser Objekt schicken
        for (int i = 0; i < this.onlineUsers.size(); i++) {
            this.getUser(this.onlineUsers.get(i)).getClientHandler().sendNewUserLoggedOn(usr, room);
        }
    }
    
    
    // -------------------------------------------------------------------------
    // an alle onlineUser LogOut Objekt schicken
    private void sendUserLoggedOffToAll(String usr, String room) {
        for (int i = 0; i < this.onlineUsers.size(); i++) {
            this.getUser(this.onlineUsers.get(i)).getClientHandler().sendUserLoggedOff(usr, room);
        }
    }
    
    
    // -------------------------------------------------------------------------
    // alle online User an User schicken
    public String[] getAllOnlineUser() {
        
        String[] allOnUsers = new String[this.onlineUsers.size()];
        
        for (int i = 0; i < this.onlineUsers.size(); i++) { 
            allOnUsers[i] = this.getUser(this.onlineUsers.get(i)).getUserName();
        }
        return allOnUsers;
    }
    
    
    // -------------------------------------------------------------------------
    // Nachricht an alle User schicken
    public void sendMessageToAllUser(Message msg) {
        for (int i = 0; i < this.onlineUsers.size(); i++) {
            this.getUser(this.onlineUsers.get(i)).getClientHandler().sendMessageToClient(msg);
        }
        serverGUI.setMessage(msg);
    }    
    
    
    // -------------------------------------------------------------------------
    // Alle User ausloggen
    public void sendLogOutToAllUser(User.LogOutUser.LogoutReason reason, boolean serverNotification) {
        String[] onlineClients = new String[onlineUsers.size()];
        onlineUsers.toArray(onlineClients);
        for (int i = 0; i < onlineClients.length; i++) {
            this.getUser(onlineClients[i]).getClientHandler().sendLogOutToUser(reason, serverNotification);
        }
    }  
    
    
    // -------------------------------------------------------------------------
    // Alle User über neuen Raum benachrichtigen
    public void informAllUserAboutNewRoom(String roomName) {
        for (int i = 0; i < this.onlineUsers.size(); i++) {
            this.getUser(this.onlineUsers.get(i)).getClientHandler().informUserAboutNewRoom(roomName);
        }
    }
    
    
    // -------------------------------------------------------------------------
    // Alle User über gelöschten Raum informieren
    public void informAllUserAboutDeletedRoom(String roomName) {
        for (int i = 0; i < this.onlineUsers.size(); i++) {
            this.getUser(this.onlineUsers.get(i)).getClientHandler().informUserAboutDeletedRoom(roomName);
        }
        for(String temp: onlineUsers) {
            if(users.get(temp).getRoomName().compareTo(roomName) == 0) {
                users.get(temp).setRoom(null);
            }
        }
    }
    
    
    // -------------------------------------------------------------------------
    // Alle User über Änderung des Raumnamens informieren
    public void informAllUserAboutRoomNameChanged(String oldName, String newName) {
        for (int i = 0; i < this.onlineUsers.size(); i++) {
            this.getUser(this.onlineUsers.get(i)).getClientHandler().informUserAboutRoomNameChanged(oldName, newName);
        }
        for(String temp: onlineUsers) {
            if(users.get(temp).getRoomName().compareTo(oldName) == 0) {
                users.get(temp).setRoom(newName);
            }
        }
    }
    
    
    // -------------------------------------------------------------------------
    // Raumnamenänderung den Usern mitteilen
    public void informAllUserAboutUserChangedRoom(String userName, String newRoomName) {
        for (int i = 0; i < this.onlineUsers.size(); i++) {
            if(this.onlineUsers.get(i).compareTo(userName) != 0) {
                this.getUser(this.onlineUsers.get(i)).getClientHandler().sendUserChangedRoom(userName, newRoomName);
            }
        }
    }
    
    
    // -------------------------------------------------------------------------
    // alle User für Export übergeben
    public String[][] getAllUserForExport() {
        
        String[][] allUser = new String[this.users.size()][3];
        int i = 0;
        
        for(String key : this.users.keySet()) {
            String[] parameter = {key, this.users.get(key).getPassword(), this.users.get(key).getUserStatus().toString()};
            allUser[i++] = parameter;
        }     
        return allUser;
    }
   
    
    // -------------------------------------------------------------------------
    // Export von User
    public void userExport(){
        String[][] userNames = getAllUserForExport();
        if(userNames != null && userNames.length > 0) { //nur wenn user vorhanden sind weiter machen
            FileWriter writer = null;
            try {
                writer = new FileWriter(System.getProperty("user.dir")+"\\Data\\user.ini", false);
                
                for(int i=0; i<userNames.length; i++) {
                    String line = userNames[i][0]; //Username schreiben
                    line += "\t";
                    line += userNames[i][1]; //Passwort schreiben
                    line += "\t";
                    line += userNames[i][2]; //Status schreiben
                    line += "\r\n";
                    
                    writer.write(line);
                }
            } catch (IOException ex) {
                PopUp fehler = new PopUp(serverGUI, "IOException : " + ex.getMessage(), "Fehler in UserExport", PopUp.MessageTyp.Error);
            } finally {
                try {
                    writer.close();
                } catch (IOException ex) {
                    PopUp fehler = new PopUp(serverGUI, "IOException beim Schließen des Writer: " + ex.getMessage(), "Fehler in UserExport", PopUp.MessageTyp.Error);
                }
            }
        }
    }
    
    
    // -------------------------------------------------------------------------
    // Import von User
    public boolean userImport(boolean showNotes){
        boolean returnStatement = false;
        
        File datei = new File(System.getProperty("user.dir")+"\\Data\\user.ini");
        if(!datei.exists()) {  //Falls Datei nicht existiert
            if(showNotes) {
                PopUp fehler = new PopUp(serverGUI, "Es wurden keine Nutzerdaten geladen, da keine user.ini vorhanden ist");
            }
            return false;
        }
        FileReader reader;
        BufferedReader textReader = null;
        try {
            reader = new FileReader(datei.getPath());
            textReader = new BufferedReader(reader);
            
            String line = textReader.readLine();
            while(line != null) {
                returnStatement = true;
                int tab1 = line.indexOf("\t", 0);
                int tab2 = line.indexOf("\t", tab1+1);
                String name = line.substring(0, tab1);
                int passwort = Integer.parseInt(line.substring(tab1+1, tab2));
                String status = line.substring(tab2+1, line.length());
                
                User user = new User(name, passwort, Enum.valueOf(new User("23","42").getUserStatus().getDeclaringClass(), status));
                this.registrate(user, false);
                
                line = textReader.readLine();
            }
            
        } catch (FileNotFoundException ex) {
            PopUp fehler = new PopUp(serverGUI, "Datei wurde nicht gefunden: " + ex.getMessage(), "Fehler beim Laden der Nutzerdaten", PopUp.MessageTyp.Error);
        } catch (IOException ex) {
            PopUp fehler = new PopUp(serverGUI, "IOException : " + ex.getMessage(), "Fehler in UserImport", PopUp.MessageTyp.Error);
        } finally {
            try {
                textReader.close();
            } catch (IOException ex) {
                PopUp fehler = new PopUp(serverGUI, "Stream konnte nicht geschlossen werden: " + ex.getMessage(), "Fehler in UserImport", PopUp.MessageTyp.Error);
            }
        }
        return returnStatement;
    }
    
     
    // -------------------------------------------------------------------------
    // Nachricht an bestimmten User schicken
    public void sendMessageToClient (String name, Message mes) {
        this.getUser(name).getClientHandler().sendMessageToClient(mes);
        this.serverGUI.setMessage(mes);
    }

    public ServerGui getGui() {
        return serverGUI;
    }
}